Data Download


  • My choice of dataset is GSE209552.
# Define global constant
GSE_NUM <- "GSE209552"

GEOmetadb setup

To setup GEOmetadb Tutorial, follow GEOmetadb Tutorial Chapter 3

# Install required package if not installed
if (!requireNamespace("BiocManager", quietly = TRUE))
  install.packages("BiocManager")
if (!requireNamespace("GEOmetadb", quietly = TRUE))
  BiocManager::install("GEOmetadb")
if (!requireNamespace("DBI", quietly = TRUE))
  install.packages("DBI")
if (!requireNamespace("RSQLite", quietly = TRUE))
  install.packages("RSQLite")
if (!requireNamespace("dplyr", quietly = TRUE))
  install.packages("dplyr")
if (!requireNamespace("ggplot2", quietly = TRUE))
  install.packages("ggplot2")

# Attach packages
library(GEOmetadb)
library(DBI)
library(RSQLite)

# Download SQLite database if not exist
if (!file.exists('GEOmetadb.sqlite')) getSQLiteFile()
file.info('GEOmetadb.sqlite')

# Connect to database
con <- dbConnect(SQLite(), 'GEOmetadb.sqlite')

Download whole GSE209552

Download data, follow Identifier Mapping Tutorial. The helper function defined below is modified based on Identifier Mapping Tutorial’s helper function to avoid duplicated download.

# Define helper function
fetch_geo_supp <- function(gse, destdir = "data") {
  # Calculate the actual path where GEOquery puts the files
  target_path <- file.path(destdir, gse)
  
  # Check if directory exists AND contains files
  if (dir.exists(target_path) && length(list.files(target_path)) > 0) {
    message(paste("Data found at", target_path, "- Skipping download."))
    return(invisible(target_path))
  }
  
  # If missing or empty, proceed with download
  message(paste("Downloading", gse, "to", destdir, "..."))
  dir.create(destdir, showWarnings = FALSE, recursive = TRUE)
  GEOquery::getGEOSuppFiles(GEO = gse, baseDir = destdir, makeDirectory = TRUE)
  
  invisible(target_path)
}

# Download
fetch_geo_supp(gse = GSE_NUM)

Construct scRNA-seq only reference list

This GSE contain both scRNA-seq and bulk RNA-seq sample. Removal of bulk RNA-seq is needed to avoid significant batch effect due to their technical difference. Although the number of sample is reduced to 17, it’s still sufficient amount.

Refer to the original paper’s method section, only the scRNA-seq was done by Illumina NextSeq6000 platform, GPL24676. We can use this clue to exclude all bulk RNA-seq

Since all GSMs of this study are packed in one GSE209552_RAW.tar file in GEO, we can’t indicate which platform to include/exclude at download step. Thus we will construct a list of GPL24676 GSMs for downstream reference by query GEOmetabd.

# Define desired platform
scRNA_platform <- "GPL24676"

# Construct the query
# We join the 'link' table (gse_gsm) with the 'sample' table (gsm) 
# to check the platform (gpl) column.
sql_filter <- paste0(
  "SELECT t1.gsm ",
  "FROM gse_gsm t1 ",
  "JOIN gsm t2 ON t1.gsm = t2.gsm ",
  "WHERE t1.gse = '", GSE_NUM, "' ",
  "AND t2.gpl = '", scRNA_platform, "'"
)

# Get the list of pure scRNA-seq samples
sc_gsm_list <- dbGetQuery(con, sql_filter)$gsm

# View results
print(paste("Found", length(sc_gsm_list), "scRNA-seq samples."))
[1] "Found 17 scRNA-seq samples."
sc_gsm_list
 [1] "GSM6376803" "GSM6376804" "GSM6376805" "GSM6376806"
 [5] "GSM6376807" "GSM6376811" "GSM6376812" "GSM6376813"
 [9] "GSM6376814" "GSM6376815" "GSM6376816" "GSM6376817"
[13] "GSM6376818" "GSM6376819" "GSM6376820" "GSM6376821"
[17] "GSM6376822"

Access data quality

# Install Seurat if not
if (!requireNamespace("Seurat", quietly = TRUE))
  install.packages("Seurat")

Reorganize scRNA-seq files

The raw count data was stored in GSE209552_RAW.tar file, thus need to be unpacked using untar() function.

# Attach packages
library(Seurat)
library(data.table)
library(Matrix)
library(dplyr)
library(stringr)

# Define data paths
tar_file <- "data/GSE209552/GSE209552_RAW.tar"
extract_dir <- "data/GSE209552/raw_files"

# Define unpack helper function
unpack_geo_tar <- function(tar_path, dest_dir) {
  if (dir.exists(dest_dir) && length(list.files(dest_dir)) > 0) {
    message(paste("📂 Extracted files found at", dest_dir, "- Skipping unpack."))
    return(invisible(dest_dir))
  }
  if (!file.exists(tar_path)) {
    stop(paste("❌ Tar file not found:", tar_path))
  }
  message(paste("📦 Unpacking", basename(tar_path), "..."))
  dir.create(dest_dir, showWarnings = FALSE, recursive = TRUE)
  untar(tar_path, exdir = dest_dir)
  message("✅ Unpacking complete.")
  return(invisible(dest_dir))
}

# Run the unpacker
unpack_geo_tar(tar_file, extract_dir)

The unpacked file is very messy, contain all 10x genomics scRNA-seq files, bulk RNA-seq files, subsetted Transposable Elements file, clustered files, and normalized files. Among those, only 10x genomics is what we need.

To extract, we utilize previously defined sc_gsm_list, loop over it, exclude any file contain _TE_, indicate Transposable Elements, only extract files end with either barcode, matrix, or features. Lastly, trim the file name to barcode, matrix, features only, to meet the expected naming format of Seurat’s Read10X() function.

# Reorganize all scRNA-seq 10x files in to a new directory
dest_dir <- "data/GSE209552/10x_organized"    # Where they should go
dir.create(dest_dir, recursive = TRUE, showWarnings = FALSE)

# --- MAIN LOOP ---
message(paste("🚀 Organizing files for", length(sc_gsm_list), "samples..."))

skipped_count <- 0
moved_count   <- 0

for (gsm in sc_gsm_list) {
  
  # 1. Define the specific destination path for this sample
  sample_subdir <- file.path(dest_dir, gsm)
  
  # 2. CHECK: Does it already exist?
  if (dir.exists(sample_subdir) && length(list.files(sample_subdir)) >= 3) {
    skipped_count <- skipped_count + 1
    next
  }
  
  # 3. Create directory
  dir.create(sample_subdir, showWarnings = FALSE)
  
  # 4. Find source files (Exclude the 'TE' files)
  pattern <- paste0("^", gsm)
  files_to_move <- list.files(extract_dir, pattern = pattern, full.names = TRUE)
  files_to_move <- files_to_move[!grepl("_TE_", files_to_move)]
  
  # Safety Checks
  if (length(files_to_move) == 0) {
    warning(paste("⚠️ Skipping", gsm, "- No source files found."))
    next
  }
  
  # 5. MOVE & RENAME (The Standardize Step)
  # We identify specific files to ensure we rename them correctly for Seurat
  f_matrix   <- files_to_move[grep("matrix", files_to_move, ignore.case = TRUE)]
  f_barcodes <- files_to_move[grep("barcodes", files_to_move, ignore.case = TRUE)]
  f_features <- files_to_move[grep("features|genes", files_to_move, ignore.case = TRUE)]
  
  # Verify we found exactly 1 of each before moving
  if (length(f_matrix) == 1 && length(f_barcodes) == 1 && length(f_features) == 1) {
    
    # Rename Matrix -> matrix.mtx.gz
    file.rename(f_matrix, file.path(sample_subdir, "matrix.mtx.gz"))
    
    # Rename Barcodes -> barcodes.tsv.gz
    file.rename(f_barcodes, file.path(sample_subdir, "barcodes.tsv.gz"))
    
    # Rename Genes/Features -> features.tsv.gz
    # (Note: Seurat prefers 'features.tsv' over 'genes.tsv')
    file.rename(f_features, file.path(sample_subdir, "features.tsv.gz"))
    
    message(paste("  ✅ Standardized & Moved:", gsm))
    moved_count <- moved_count + 1
    
  } else {
    warning(paste("  ⚠️ Skipping", gsm, "- Could not find unique 10x triplet to standardize."))
  }
}

# --- SUMMARY ---
message("🎉 Organization Complete.")
message(paste("  🔹 Samples moved & standardized:", moved_count))
message(paste("  🔸 Samples skipped (already done):", skipped_count))

Load scRNA-seq files into R using Seurat

Loading of expression data is done by iteratively call Seurat’s Read10X() and CreateSeuratObject() function over all GSMs. They cooperatively first read a 10X genomics structured file and create a Seurat Object, which in the basic unit for any data manipulation in Seurat.

# Get the list of sample subdirectories
sample_dirs <- list.dirs(dest_dir, recursive = FALSE)

# Define a list storing seurat obj of all GSMs
seurat_list <- list()

message(paste("🚀 Loading", length(sample_dirs), "samples..."))
for (dir in sample_dirs) {
  # Extract GSM ID from the folder name (e.g., "GSM6385438")
  gsm_id <- basename(dir)
  
  # A. Read 10x Data
  # Because you renamed the files, Read10X finds them automatically!
  counts <- Read10X(data.dir = dir)
  
  # B. Create Seurat Object
  seurat_list[[gsm_id]] <- CreateSeuratObject(
    counts = counts, 
    project = gsm_id, 
    min.cells = 0, 
    min.features = 0
  )
  
  message(paste("  ✅ Loaded:", gsm_id))
}
length(seurat_list)
[1] 17

However, the metadata (contain condition information) need to fetched from GEOmetadb, and extract from GSM table’s characteristics_ch1 column, and then insert into corresponding Seurat object’s metadata layer.

# Load GSMs metadata from GEOmetadb
gsm_list_string <- paste(paste0("'", sc_gsm_list, "'"), collapse = ",")
sql <- paste0(
  "SELECT gsm, title, characteristics_ch1 ",
  "FROM gsm ",
  "WHERE gsm IN (", gsm_list_string, ")"
)
metadata <- dbGetQuery(con, sql)

# Merge the condition metadata to corresponding Seurat obj's metadata
metadata$Condition <- sub(".*condition:\\s*([^;]+).*", "\\1", metadata$characteristics_ch1, ignore.case = TRUE)

# Loop and assign
for (gsm in names(seurat_list)) {
  # Find the condition for this GSM
  val <- metadata$Condition[metadata$gsm == gsm]
  
  # Assign to Seurat object (handling missing values)
  if (length(val) > 0) {
    seurat_list[[gsm]]$Condition <- val
  } else {
    seurat_list[[gsm]]$Condition <- "Unknown"
  }
}

# Final Merge all samples into one seurat obj
combined_seurat <- merge(
 x = seurat_list[[1]], 
 y = seurat_list[-1], 
 add.cell.ids = names(seurat_list), 
 project = "GSE209552"
)
# Verify
table(combined_seurat$Condition)

control     tbi 
  10666   13955 

Data exploriation

Plot the following statistics:

  • Number of unique identifier (infer coverage)
  • Number of total amount of identifier (infer depth)
  • Number of samples
  • Number of cells
library(ggplot2)
library(patchwork)

theme_set(theme_classic(base_size = 26))

# 1. Coverage (nFeature_RNA)
p_cov <- VlnPlot(
  combined_seurat, 
  features = "nFeature_RNA", 
  group.by = "Condition", 
  pt.size = 0
) + 
  ggtitle("Coverage") + 
  ylab("nFeature_RNA")

# 2. Depth (nCount_RNA) with adjusted axis
p_depth <- VlnPlot(
  combined_seurat, 
  features = "nCount_RNA", 
  group.by = "Condition", 
  pt.size = 0
) + 
  ggtitle("Depth") + 
  ylab("nCount_RNA") +
  scale_y_continuous(limits = c(0, 50000)) 

# Combine them to recreate p_metrics
p_metrics <- p_cov | p_depth

# Number of cells among conditions
meta_data <- combined_seurat@meta.data

# A. Bar plot: Total Number of Cells per Condition
p_cells <- ggplot(meta_data, aes(x = Condition, fill = Condition)) +
  geom_bar(color = "black") +
  geom_text(stat = 'count', aes(label = after_stat(count)), vjust = -0.5) +
  theme_classic() +
  labs(title = "Total Cells per Group", y = "Number of Cells", x = NULL) +
  theme(legend.position = "none")

# Number of samples among conditions
p_samples <- meta_data %>%
  group_by(Condition) %>%
  summarize(Sample_Count = n_distinct(orig.ident)) %>%
  ggplot(aes(x = Condition, y = Sample_Count, fill = Condition)) +
  geom_bar(stat = "identity", color = "black") +
  geom_text(aes(label = Sample_Count), vjust = -0.5) +
  theme_classic() +
  labs(title = "Total Samples (GSMs) per Group", y = "Number of Samples (GSMs)", x = NULL) +
  theme(legend.position = "none")

# Merge plots
final_plot <- p_metrics / (p_samples | p_cells)

# Adjust font size
final_plot <- final_plot & theme_classic(base_size = 16)
final_plot

Figure 1: Overall statistics of dataset GSE209552 grouped by condition (Control v.s. tbi). The upper-left panel indicate the number of unique identifier; The upper-right panel indicate the total amount of identifier, the maximum amount of identifier was limited up to 50000 for the sake of aesthetics; The lower-left panel indicate number of samples (GSMs); The lower-right panel indicate the number of cells”; X-axis of all four panel indicate condition.

Generally, control group have higher depth and coverage, but less sample and cells.

Then plot the statistics below per sample (GSM):

  • Number of unique identifier (infer coverage)
  • Number of total amount of identifier (infer depth)
  • Number of cells

# Define the vertical line style once to keep it consistent
vertical_line <- geom_vline(xintercept = 5.5, linetype = "dashed", color = "red", size = 1)

# 1. Coverage (nFeature_RNA)
p_cov <- VlnPlot(
  combined_seurat, 
  features = "nFeature_RNA", 
  group.by = "orig.ident", 
  pt.size = 0
) + 
  ggtitle("Coverage") + 
  ylab("nFeature_RNA") +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none",
    axis.title.x = element_blank() # Often cleaner to remove x-label if text is rotated
  ) + vertical_line

# 2. Depth (nCount_RNA) with adjusted axis
p_depth <- VlnPlot(
  combined_seurat, 
  features = "nCount_RNA", 
  group.by = "orig.ident", 
  pt.size = 0
) + 
  ggtitle("Depth") + 
  ylab("nCount_RNA") +
  scale_y_continuous(limits = c(0, 50000)) +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none",
    axis.title.x = element_blank()
  ) + vertical_line

# A. Bar plot: Total Number of Cells per Condition
meta_data <- combined_seurat@meta.data # Ensure meta_data is defined
p_cells <- ggplot(meta_data, aes(x = orig.ident, fill = orig.ident)) +
  geom_bar(color = "black") +
  geom_text(stat = 'count', aes(label = after_stat(count)), vjust = -0.5) +
  theme_classic() +
  labs(title = "Total Cells per Sample", y = "Number of Cells", x = NULL) +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none"
  ) +
  coord_fixed(ratio = 0.0005)  +
  scale_y_continuous(limits = c(0, 6000)) + vertical_line

# Merge plots
final_plot <- p_cov / p_depth / p_cells + 
  plot_layout(heights = c(1, 1, 1))

# Adjust font size (and re-apply theme elements if needed by the base theme)
final_plot <- final_plot & theme_classic(base_size = 16) & 
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none"
  )

final_plot

Figure 2: Overall statistics per sample (GSM). The top panel indicate number of unique identifier; The middle panel indicate number of total identifier; The bottom panel indicate number of cells; X-axis of all three panel indicate samples (GSMs); The vertial red dash line sepeate control (left side of red line), and tbi (right side of red line).

In general, the sequence depth and coverage is more consistence among control group. Tbi group has more variated depth and coverage with GSM637681 being the sample of lowest depths and coverage. The number of cells within different sample also varies, from highest 3708 (GSM6376822) to lowest 358 (GSM6376820). This imbalance in cell number may result in downstream analysis dominate by sample which contain more cells.

Another common quality control step in scRNA-seq is to remove damaged cells. When damaged cells were sequenced or damaged during sequencing, the expression intensity will scaled down due to leaked RNA, thus significantly bias downstream clustering, annotation, and differential gene expression analysis. A common method to conduct this filtering is using mitochondrial transcript percentage, based on this idea that if a cell has leaked RNA, the mitochondrial transcript percentage will be relatively higher since mitochondria is relatively large and less likely to leak.

# Use seurat PercentageFeatureSet() function
# Create a new entry in meta.data layer storing mitochondrial transcript percentage
combined_seurat[["percent.mt"]] <- PercentageFeatureSet(combined_seurat, pattern = "^MT-")
VlnPlot(combined_seurat, features = c("percent.mt")) +
  theme(
    # Rotate x-axis labels 45 degrees so they don't overlap
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "none" # Hide legend since x-axis already shows the names
  ) + vertical_line

Figure 3: Mitochondrial transcript percentage grouped by GSMs. The Y-axis indicate mitochondrial transcript percentage; The X-axis indicate GSM; The vertial red dash line sepeate control (left side of red line), and tbi (right side of red line).

The mitochondrial transcript percentage aligned with previous depth and coverage among samples, where control group tend to be more consistent. A hypothesis is that Traumatic Brain Injury (TBI) is a broad term that include various brain damage result from external force, and the intensity and exact pathology may vary among different tbi sample, reflected by sequence depth and coverage. Noticeably, GSM6376814 has relatively higher mitochondrial transcript percentage, and recall that it’s also the sample having lowest depth and coverage, which might imply existing technical variance that could bias downstream analysis.

LS0tCnRpdGxlOiAnQXNzaWdubWVudCAjMScKYXV0aG9yOiAiSmlhcWkgTWEiCmRhdGU6ICIyMDI2LTAyLTA2IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAnMicKICAgIGRmX3ByaW50OiBwYWdlZAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IG5vbmUKYmlibGlvZ3JhcGh5OiBCQ0I0MjAuYmliCmxpbmstY2l0YXRpb25zOiB0cnVlCi0tLQoKIyBEYXRhIERvd25sb2FkCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0gICBNeSBjaG9pY2Ugb2YgZGF0YXNldCBpcyBgR1NFMjA5NTUyYC4KCmBgYHtyfQojIERlZmluZSBnbG9iYWwgY29uc3RhbnQKR1NFX05VTSA8LSAiR1NFMjA5NTUyIgpgYGAKCiMjIEdFT21ldGFkYiBzZXR1cAoKVG8gc2V0dXAgW0dFT21ldGFkYiBUdXRvcmlhbF0oaHR0cHM6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvaHRtbC9HRU9tZXRhZGIuaHRtbCksIGZvbGxvdyBbR0VPbWV0YWRiIFR1dG9yaWFsIENoYXB0ZXIgM10oaHR0cHM6Ly9iY2I0MjAtMjAyNi5naXRodWIuaW8vRXhlcmNpc2VfRmluZGluZ19FeHByZXNzaW9uX2RhdGEvc2V0dGluZy11cC1nZW9tZXRhZGIuaHRtbCkKCmBgYHtyfQojIEluc3RhbGwgcmVxdWlyZWQgcGFja2FnZSBpZiBub3QgaW5zdGFsbGVkCmlmICghcmVxdWlyZU5hbWVzcGFjZSgiQmlvY01hbmFnZXIiLCBxdWlldGx5ID0gVFJVRSkpCiAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkdFT21ldGFkYiIsIHF1aWV0bHkgPSBUUlVFKSkKICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiR0VPbWV0YWRiIikKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJEQkkiLCBxdWlldGx5ID0gVFJVRSkpCiAgaW5zdGFsbC5wYWNrYWdlcygiREJJIikKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJSU1FMaXRlIiwgcXVpZXRseSA9IFRSVUUpKQogIGluc3RhbGwucGFja2FnZXMoIlJTUUxpdGUiKQppZiAoIXJlcXVpcmVOYW1lc3BhY2UoImRwbHlyIiwgcXVpZXRseSA9IFRSVUUpKQogIGluc3RhbGwucGFja2FnZXMoImRwbHlyIikKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJnZ3Bsb3QyIiwgcXVpZXRseSA9IFRSVUUpKQogIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQoKIyBBdHRhY2ggcGFja2FnZXMKbGlicmFyeShHRU9tZXRhZGIpCmxpYnJhcnkoREJJKQpsaWJyYXJ5KFJTUUxpdGUpCgojIERvd25sb2FkIFNRTGl0ZSBkYXRhYmFzZSBpZiBub3QgZXhpc3QKaWYgKCFmaWxlLmV4aXN0cygnR0VPbWV0YWRiLnNxbGl0ZScpKSBnZXRTUUxpdGVGaWxlKCkKZmlsZS5pbmZvKCdHRU9tZXRhZGIuc3FsaXRlJykKCiMgQ29ubmVjdCB0byBkYXRhYmFzZQpjb24gPC0gZGJDb25uZWN0KFNRTGl0ZSgpLCAnR0VPbWV0YWRiLnNxbGl0ZScpCmBgYAoKIyMgRG93bmxvYWQgd2hvbGUgR1NFMjA5NTUyCgpEb3dubG9hZCBkYXRhLCBmb2xsb3cgW0lkZW50aWZpZXIgTWFwcGluZyBUdXRvcmlhbF0oaHR0cHM6Ly9iY2I0MjAtMjAyNi5naXRodWIuaW8vRXhhbXBsZV9TdHVkZW50L2lkX21hcHBpbmcvZ2V0dGluZy1kYXRhLWZyb20tZ2VvLmh0bWwpLiBUaGUgaGVscGVyIGZ1bmN0aW9uIGRlZmluZWQgYmVsb3cgaXMgbW9kaWZpZWQgYmFzZWQgb24gW0lkZW50aWZpZXIgTWFwcGluZyBUdXRvcmlhbCdzIGhlbHBlciBmdW5jdGlvbl0oaHR0cHM6Ly9naXRodWIuY29tL2JjYjQyMC0yMDI2L0V4YW1wbGVfU3R1ZGVudC9ibG9iL21haW4vaW5fY2xhc3NfZXhlcmNpc2VzL2lkX21hcHBpbmcvZmV0Y2hfZ2VvX3N1cHAuUikgdG8gYXZvaWQgZHVwbGljYXRlZCBkb3dubG9hZC4KCmBgYHtyfQojIERlZmluZSBoZWxwZXIgZnVuY3Rpb24KZmV0Y2hfZ2VvX3N1cHAgPC0gZnVuY3Rpb24oZ3NlLCBkZXN0ZGlyID0gImRhdGEiKSB7CiAgIyBDYWxjdWxhdGUgdGhlIGFjdHVhbCBwYXRoIHdoZXJlIEdFT3F1ZXJ5IHB1dHMgdGhlIGZpbGVzCiAgdGFyZ2V0X3BhdGggPC0gZmlsZS5wYXRoKGRlc3RkaXIsIGdzZSkKICAKICAjIENoZWNrIGlmIGRpcmVjdG9yeSBleGlzdHMgQU5EIGNvbnRhaW5zIGZpbGVzCiAgaWYgKGRpci5leGlzdHModGFyZ2V0X3BhdGgpICYmIGxlbmd0aChsaXN0LmZpbGVzKHRhcmdldF9wYXRoKSkgPiAwKSB7CiAgICBtZXNzYWdlKHBhc3RlKCJEYXRhIGZvdW5kIGF0IiwgdGFyZ2V0X3BhdGgsICItIFNraXBwaW5nIGRvd25sb2FkLiIpKQogICAgcmV0dXJuKGludmlzaWJsZSh0YXJnZXRfcGF0aCkpCiAgfQogIAogICMgSWYgbWlzc2luZyBvciBlbXB0eSwgcHJvY2VlZCB3aXRoIGRvd25sb2FkCiAgbWVzc2FnZShwYXN0ZSgiRG93bmxvYWRpbmciLCBnc2UsICJ0byIsIGRlc3RkaXIsICIuLi4iKSkKICBkaXIuY3JlYXRlKGRlc3RkaXIsIHNob3dXYXJuaW5ncyA9IEZBTFNFLCByZWN1cnNpdmUgPSBUUlVFKQogIEdFT3F1ZXJ5OjpnZXRHRU9TdXBwRmlsZXMoR0VPID0gZ3NlLCBiYXNlRGlyID0gZGVzdGRpciwgbWFrZURpcmVjdG9yeSA9IFRSVUUpCiAgCiAgaW52aXNpYmxlKHRhcmdldF9wYXRoKQp9CgojIERvd25sb2FkCmZldGNoX2dlb19zdXBwKGdzZSA9IEdTRV9OVU0pCmBgYAoKIyMgQ29uc3RydWN0IHNjUk5BLXNlcSBvbmx5IHJlZmVyZW5jZSBsaXN0CgpUaGlzIEdTRSBjb250YWluIGJvdGggc2NSTkEtc2VxIGFuZCBidWxrIFJOQS1zZXEgc2FtcGxlLiBSZW1vdmFsIG9mIGJ1bGsgUk5BLXNlcSBpcyBuZWVkZWQgdG8gYXZvaWQgc2lnbmlmaWNhbnQgYmF0Y2ggZWZmZWN0IGR1ZSB0byB0aGVpciB0ZWNobmljYWwgZGlmZmVyZW5jZS4gQWx0aG91Z2ggdGhlIG51bWJlciBvZiBzYW1wbGUgaXMgcmVkdWNlZCB0byAxNywgaXQncyBzdGlsbCBzdWZmaWNpZW50IGFtb3VudC4KClJlZmVyIHRvIHRoZSBbb3JpZ2luYWwgcGFwZXJdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDE2L2ouY2VscmVwLjIwMjMuMTEzMzk1KSdzIG1ldGhvZCBzZWN0aW9uLCBvbmx5IHRoZSBzY1JOQS1zZXEgd2FzIGRvbmUgYnkgYElsbHVtaW5hIE5leHRTZXE2MDAwYCBwbGF0Zm9ybSwgW0dQTDI0Njc2XShodHRwczovL3d3dy1uY2JpLW5sbS1uaWgtZ292Lm15YWNjZXNzLmxpYnJhcnkudXRvcm9udG8uY2EvZ2VvL3F1ZXJ5L2FjYy5jZ2k/YWNjPUdQTDI0Njc2KS4gV2UgY2FuIHVzZSB0aGlzIGNsdWUgdG8gZXhjbHVkZSBhbGwgYnVsayBSTkEtc2VxCgpTaW5jZSBhbGwgR1NNcyBvZiB0aGlzIHN0dWR5IGFyZSBwYWNrZWQgaW4gb25lIGBHU0UyMDk1NTJfUkFXLnRhcmAgZmlsZSBpbiBHRU8sIHdlIGNhbid0IGluZGljYXRlIHdoaWNoIHBsYXRmb3JtIHRvIGluY2x1ZGUvZXhjbHVkZSBhdCBkb3dubG9hZCBzdGVwLiBUaHVzIHdlIHdpbGwgY29uc3RydWN0IGEgbGlzdCBvZiBgR1BMMjQ2NzZgIEdTTXMgZm9yIGRvd25zdHJlYW0gcmVmZXJlbmNlIGJ5IHF1ZXJ5IGBHRU9tZXRhYmRgLgoKYGBge3J9CiMgRGVmaW5lIGRlc2lyZWQgcGxhdGZvcm0Kc2NSTkFfcGxhdGZvcm0gPC0gIkdQTDI0Njc2IgoKIyBDb25zdHJ1Y3QgdGhlIHF1ZXJ5CiMgV2Ugam9pbiB0aGUgJ2xpbmsnIHRhYmxlIChnc2VfZ3NtKSB3aXRoIHRoZSAnc2FtcGxlJyB0YWJsZSAoZ3NtKSAKIyB0byBjaGVjayB0aGUgcGxhdGZvcm0gKGdwbCkgY29sdW1uLgpzcWxfZmlsdGVyIDwtIHBhc3RlMCgKICAiU0VMRUNUIHQxLmdzbSAiLAogICJGUk9NIGdzZV9nc20gdDEgIiwKICAiSk9JTiBnc20gdDIgT04gdDEuZ3NtID0gdDIuZ3NtICIsCiAgIldIRVJFIHQxLmdzZSA9ICciLCBHU0VfTlVNLCAiJyAiLAogICJBTkQgdDIuZ3BsID0gJyIsIHNjUk5BX3BsYXRmb3JtLCAiJyIKKQoKIyBHZXQgdGhlIGxpc3Qgb2YgcHVyZSBzY1JOQS1zZXEgc2FtcGxlcwpzY19nc21fbGlzdCA8LSBkYkdldFF1ZXJ5KGNvbiwgc3FsX2ZpbHRlcikkZ3NtCgojIFZpZXcgcmVzdWx0cwpwcmludChwYXN0ZSgiRm91bmQiLCBsZW5ndGgoc2NfZ3NtX2xpc3QpLCAic2NSTkEtc2VxIHNhbXBsZXMuIikpCnNjX2dzbV9saXN0CmBgYAoKIyBBY2Nlc3MgZGF0YSBxdWFsaXR5CgotICAgVGhlIGZvbGxvd2luZyBhbmFseXNpcyBpcyBidWlsZCBvbiBbU2V1cmF0XShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0LykgZnJhbWV3b3JrIGFuZCByZWZlcnJpbmcgW0hldW1vcyBldCBhbCwgMjAyM10oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzgvczQxNTc2LTAyMy0wMDU4Ni13KSBmb3IgaGlnaC1sZXZlbCBpZGVhLgoKYGBge3J9CiMgSW5zdGFsbCBTZXVyYXQgaWYgbm90CmlmICghcmVxdWlyZU5hbWVzcGFjZSgiU2V1cmF0IiwgcXVpZXRseSA9IFRSVUUpKQogIGluc3RhbGwucGFja2FnZXMoIlNldXJhdCIpCmBgYAoKIyMgUmVvcmdhbml6ZSBzY1JOQS1zZXEgZmlsZXMKClRoZSByYXcgY291bnQgZGF0YSB3YXMgc3RvcmVkIGluIGBHU0UyMDk1NTJfUkFXLnRhcmAgZmlsZSwgdGh1cyBuZWVkIHRvIGJlIHVucGFja2VkIHVzaW5nIGB1bnRhcigpYCBmdW5jdGlvbi4KCmBgYHtyfQojIEF0dGFjaCBwYWNrYWdlcwpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShkcGx5cikKbGlicmFyeShzdHJpbmdyKQoKIyBEZWZpbmUgZGF0YSBwYXRocwp0YXJfZmlsZSA8LSAiZGF0YS9HU0UyMDk1NTIvR1NFMjA5NTUyX1JBVy50YXIiCmV4dHJhY3RfZGlyIDwtICJkYXRhL0dTRTIwOTU1Mi9yYXdfZmlsZXMiCgojIERlZmluZSB1bnBhY2sgaGVscGVyIGZ1bmN0aW9uCnVucGFja19nZW9fdGFyIDwtIGZ1bmN0aW9uKHRhcl9wYXRoLCBkZXN0X2RpcikgewogIGlmIChkaXIuZXhpc3RzKGRlc3RfZGlyKSAmJiBsZW5ndGgobGlzdC5maWxlcyhkZXN0X2RpcikpID4gMCkgewogICAgbWVzc2FnZShwYXN0ZSgi8J+TgiBFeHRyYWN0ZWQgZmlsZXMgZm91bmQgYXQiLCBkZXN0X2RpciwgIi0gU2tpcHBpbmcgdW5wYWNrLiIpKQogICAgcmV0dXJuKGludmlzaWJsZShkZXN0X2RpcikpCiAgfQogIGlmICghZmlsZS5leGlzdHModGFyX3BhdGgpKSB7CiAgICBzdG9wKHBhc3RlKCLinYwgVGFyIGZpbGUgbm90IGZvdW5kOiIsIHRhcl9wYXRoKSkKICB9CiAgbWVzc2FnZShwYXN0ZSgi8J+TpiBVbnBhY2tpbmciLCBiYXNlbmFtZSh0YXJfcGF0aCksICIuLi4iKSkKICBkaXIuY3JlYXRlKGRlc3RfZGlyLCBzaG93V2FybmluZ3MgPSBGQUxTRSwgcmVjdXJzaXZlID0gVFJVRSkKICB1bnRhcih0YXJfcGF0aCwgZXhkaXIgPSBkZXN0X2RpcikKICBtZXNzYWdlKCLinIUgVW5wYWNraW5nIGNvbXBsZXRlLiIpCiAgcmV0dXJuKGludmlzaWJsZShkZXN0X2RpcikpCn0KCiMgUnVuIHRoZSB1bnBhY2tlcgp1bnBhY2tfZ2VvX3Rhcih0YXJfZmlsZSwgZXh0cmFjdF9kaXIpCmBgYAoKVGhlIHVucGFja2VkIGZpbGUgaXMgdmVyeSBtZXNzeSwgY29udGFpbiBhbGwgMTB4IGdlbm9taWNzIHNjUk5BLXNlcSBmaWxlcywgYnVsayBSTkEtc2VxIGZpbGVzLCBzdWJzZXR0ZWQgVHJhbnNwb3NhYmxlIEVsZW1lbnRzIGZpbGUsIGNsdXN0ZXJlZCBmaWxlcywgYW5kIG5vcm1hbGl6ZWQgZmlsZXMuIEFtb25nIHRob3NlLCBvbmx5IDEweCBnZW5vbWljcyBpcyB3aGF0IHdlIG5lZWQuCgpUbyBleHRyYWN0LCB3ZSB1dGlsaXplIHByZXZpb3VzbHkgZGVmaW5lZCBgc2NfZ3NtX2xpc3RgLCBsb29wIG92ZXIgaXQsIGV4Y2x1ZGUgYW55IGZpbGUgY29udGFpbiBgX1RFX2AsIGluZGljYXRlIFRyYW5zcG9zYWJsZSBFbGVtZW50cywgb25seSBleHRyYWN0IGZpbGVzIGVuZCB3aXRoIGVpdGhlciBgYmFyY29kZWAsIGBtYXRyaXhgLCBvciBgZmVhdHVyZXNgLiBMYXN0bHksIHRyaW0gdGhlIGZpbGUgbmFtZSB0byBgYmFyY29kZWAsIGBtYXRyaXhgLCBgZmVhdHVyZXNgIG9ubHksIHRvIG1lZXQgdGhlIGV4cGVjdGVkIG5hbWluZyBmb3JtYXQgb2YgW1NldXJhdF0oaHR0cHM6Ly9zYXRpamFsYWIub3JnL3NldXJhdC8pJ3MgYFJlYWQxMFgoKWAgZnVuY3Rpb24uCgpgYGB7cn0KIyBSZW9yZ2FuaXplIGFsbCBzY1JOQS1zZXEgMTB4IGZpbGVzIGluIHRvIGEgbmV3IGRpcmVjdG9yeQpkZXN0X2RpciA8LSAiZGF0YS9HU0UyMDk1NTIvMTB4X29yZ2FuaXplZCIgICAgIyBXaGVyZSB0aGV5IHNob3VsZCBnbwpkaXIuY3JlYXRlKGRlc3RfZGlyLCByZWN1cnNpdmUgPSBUUlVFLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKCiMgLS0tIE1BSU4gTE9PUCAtLS0KbWVzc2FnZShwYXN0ZSgi8J+agCBPcmdhbml6aW5nIGZpbGVzIGZvciIsIGxlbmd0aChzY19nc21fbGlzdCksICJzYW1wbGVzLi4uIikpCgpza2lwcGVkX2NvdW50IDwtIDAKbW92ZWRfY291bnQgICA8LSAwCgpmb3IgKGdzbSBpbiBzY19nc21fbGlzdCkgewogIAogICMgMS4gRGVmaW5lIHRoZSBzcGVjaWZpYyBkZXN0aW5hdGlvbiBwYXRoIGZvciB0aGlzIHNhbXBsZQogIHNhbXBsZV9zdWJkaXIgPC0gZmlsZS5wYXRoKGRlc3RfZGlyLCBnc20pCiAgCiAgIyAyLiBDSEVDSzogRG9lcyBpdCBhbHJlYWR5IGV4aXN0PwogIGlmIChkaXIuZXhpc3RzKHNhbXBsZV9zdWJkaXIpICYmIGxlbmd0aChsaXN0LmZpbGVzKHNhbXBsZV9zdWJkaXIpKSA+PSAzKSB7CiAgICBza2lwcGVkX2NvdW50IDwtIHNraXBwZWRfY291bnQgKyAxCiAgICBuZXh0CiAgfQogIAogICMgMy4gQ3JlYXRlIGRpcmVjdG9yeQogIGRpci5jcmVhdGUoc2FtcGxlX3N1YmRpciwgc2hvd1dhcm5pbmdzID0gRkFMU0UpCiAgCiAgIyA0LiBGaW5kIHNvdXJjZSBmaWxlcyAoRXhjbHVkZSB0aGUgJ1RFJyBmaWxlcykKICBwYXR0ZXJuIDwtIHBhc3RlMCgiXiIsIGdzbSkKICBmaWxlc190b19tb3ZlIDwtIGxpc3QuZmlsZXMoZXh0cmFjdF9kaXIsIHBhdHRlcm4gPSBwYXR0ZXJuLCBmdWxsLm5hbWVzID0gVFJVRSkKICBmaWxlc190b19tb3ZlIDwtIGZpbGVzX3RvX21vdmVbIWdyZXBsKCJfVEVfIiwgZmlsZXNfdG9fbW92ZSldCiAgCiAgIyBTYWZldHkgQ2hlY2tzCiAgaWYgKGxlbmd0aChmaWxlc190b19tb3ZlKSA9PSAwKSB7CiAgICB3YXJuaW5nKHBhc3RlKCLimqDvuI8gU2tpcHBpbmciLCBnc20sICItIE5vIHNvdXJjZSBmaWxlcyBmb3VuZC4iKSkKICAgIG5leHQKICB9CiAgCiAgIyA1LiBNT1ZFICYgUkVOQU1FIChUaGUgU3RhbmRhcmRpemUgU3RlcCkKICAjIFdlIGlkZW50aWZ5IHNwZWNpZmljIGZpbGVzIHRvIGVuc3VyZSB3ZSByZW5hbWUgdGhlbSBjb3JyZWN0bHkgZm9yIFNldXJhdAogIGZfbWF0cml4ICAgPC0gZmlsZXNfdG9fbW92ZVtncmVwKCJtYXRyaXgiLCBmaWxlc190b19tb3ZlLCBpZ25vcmUuY2FzZSA9IFRSVUUpXQogIGZfYmFyY29kZXMgPC0gZmlsZXNfdG9fbW92ZVtncmVwKCJiYXJjb2RlcyIsIGZpbGVzX3RvX21vdmUsIGlnbm9yZS5jYXNlID0gVFJVRSldCiAgZl9mZWF0dXJlcyA8LSBmaWxlc190b19tb3ZlW2dyZXAoImZlYXR1cmVzfGdlbmVzIiwgZmlsZXNfdG9fbW92ZSwgaWdub3JlLmNhc2UgPSBUUlVFKV0KICAKICAjIFZlcmlmeSB3ZSBmb3VuZCBleGFjdGx5IDEgb2YgZWFjaCBiZWZvcmUgbW92aW5nCiAgaWYgKGxlbmd0aChmX21hdHJpeCkgPT0gMSAmJiBsZW5ndGgoZl9iYXJjb2RlcykgPT0gMSAmJiBsZW5ndGgoZl9mZWF0dXJlcykgPT0gMSkgewogICAgCiAgICAjIFJlbmFtZSBNYXRyaXggLT4gbWF0cml4Lm10eC5negogICAgZmlsZS5yZW5hbWUoZl9tYXRyaXgsIGZpbGUucGF0aChzYW1wbGVfc3ViZGlyLCAibWF0cml4Lm10eC5neiIpKQogICAgCiAgICAjIFJlbmFtZSBCYXJjb2RlcyAtPiBiYXJjb2Rlcy50c3YuZ3oKICAgIGZpbGUucmVuYW1lKGZfYmFyY29kZXMsIGZpbGUucGF0aChzYW1wbGVfc3ViZGlyLCAiYmFyY29kZXMudHN2Lmd6IikpCiAgICAKICAgICMgUmVuYW1lIEdlbmVzL0ZlYXR1cmVzIC0+IGZlYXR1cmVzLnRzdi5negogICAgIyAoTm90ZTogU2V1cmF0IHByZWZlcnMgJ2ZlYXR1cmVzLnRzdicgb3ZlciAnZ2VuZXMudHN2JykKICAgIGZpbGUucmVuYW1lKGZfZmVhdHVyZXMsIGZpbGUucGF0aChzYW1wbGVfc3ViZGlyLCAiZmVhdHVyZXMudHN2Lmd6IikpCiAgICAKICAgIG1lc3NhZ2UocGFzdGUoIiAg4pyFIFN0YW5kYXJkaXplZCAmIE1vdmVkOiIsIGdzbSkpCiAgICBtb3ZlZF9jb3VudCA8LSBtb3ZlZF9jb3VudCArIDEKICAgIAogIH0gZWxzZSB7CiAgICB3YXJuaW5nKHBhc3RlKCIgIOKaoO+4jyBTa2lwcGluZyIsIGdzbSwgIi0gQ291bGQgbm90IGZpbmQgdW5pcXVlIDEweCB0cmlwbGV0IHRvIHN0YW5kYXJkaXplLiIpKQogIH0KfQoKIyAtLS0gU1VNTUFSWSAtLS0KbWVzc2FnZSgi8J+OiSBPcmdhbml6YXRpb24gQ29tcGxldGUuIikKbWVzc2FnZShwYXN0ZSgiICDwn5S5IFNhbXBsZXMgbW92ZWQgJiBzdGFuZGFyZGl6ZWQ6IiwgbW92ZWRfY291bnQpKQptZXNzYWdlKHBhc3RlKCIgIPCflLggU2FtcGxlcyBza2lwcGVkIChhbHJlYWR5IGRvbmUpOiIsIHNraXBwZWRfY291bnQpKQpgYGAKCiMjIExvYWQgc2NSTkEtc2VxIGZpbGVzIGludG8gUiB1c2luZyBTZXVyYXQKCkxvYWRpbmcgb2YgZXhwcmVzc2lvbiBkYXRhIGlzIGRvbmUgYnkgaXRlcmF0aXZlbHkgY2FsbCBbU2V1cmF0XShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0LykncyBgUmVhZDEwWCgpYCBhbmQgYENyZWF0ZVNldXJhdE9iamVjdCgpYCBmdW5jdGlvbiBvdmVyIGFsbCBHU01zLiBUaGV5IGNvb3BlcmF0aXZlbHkgZmlyc3QgcmVhZCBhIDEwWCBnZW5vbWljcyBzdHJ1Y3R1cmVkIGZpbGUgYW5kIGNyZWF0ZSBhIGBTZXVyYXQgT2JqZWN0YCwgd2hpY2ggaW4gdGhlIGJhc2ljIHVuaXQgZm9yIGFueSBkYXRhIG1hbmlwdWxhdGlvbiBpbiBbU2V1cmF0XShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0LykuCgpgYGB7cn0KIyBHZXQgdGhlIGxpc3Qgb2Ygc2FtcGxlIHN1YmRpcmVjdG9yaWVzCnNhbXBsZV9kaXJzIDwtIGxpc3QuZGlycyhkZXN0X2RpciwgcmVjdXJzaXZlID0gRkFMU0UpCgojIERlZmluZSBhIGxpc3Qgc3RvcmluZyBzZXVyYXQgb2JqIG9mIGFsbCBHU01zCnNldXJhdF9saXN0IDwtIGxpc3QoKQoKbWVzc2FnZShwYXN0ZSgi8J+agCBMb2FkaW5nIiwgbGVuZ3RoKHNhbXBsZV9kaXJzKSwgInNhbXBsZXMuLi4iKSkKZm9yIChkaXIgaW4gc2FtcGxlX2RpcnMpIHsKICAjIEV4dHJhY3QgR1NNIElEIGZyb20gdGhlIGZvbGRlciBuYW1lIChlLmcuLCAiR1NNNjM4NTQzOCIpCiAgZ3NtX2lkIDwtIGJhc2VuYW1lKGRpcikKICAKICAjIEEuIFJlYWQgMTB4IERhdGEKICAjIEJlY2F1c2UgeW91IHJlbmFtZWQgdGhlIGZpbGVzLCBSZWFkMTBYIGZpbmRzIHRoZW0gYXV0b21hdGljYWxseSEKICBjb3VudHMgPC0gUmVhZDEwWChkYXRhLmRpciA9IGRpcikKICAKICAjIEIuIENyZWF0ZSBTZXVyYXQgT2JqZWN0CiAgc2V1cmF0X2xpc3RbW2dzbV9pZF1dIDwtIENyZWF0ZVNldXJhdE9iamVjdCgKICAgIGNvdW50cyA9IGNvdW50cywgCiAgICBwcm9qZWN0ID0gZ3NtX2lkLCAKICAgIG1pbi5jZWxscyA9IDAsIAogICAgbWluLmZlYXR1cmVzID0gMAogICkKICAKICBtZXNzYWdlKHBhc3RlKCIgIOKchSBMb2FkZWQ6IiwgZ3NtX2lkKSkKfQpsZW5ndGgoc2V1cmF0X2xpc3QpCmBgYAoKSG93ZXZlciwgdGhlIG1ldGFkYXRhIChjb250YWluIGNvbmRpdGlvbiBpbmZvcm1hdGlvbikgbmVlZCB0byBmZXRjaGVkIGZyb20gW0dFT21ldGFkYl0oaHR0cHM6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvaHRtbC9HRU9tZXRhZGIuaHRtbCksIGFuZCBleHRyYWN0IGZyb20gYEdTTWAgdGFibGUncyBgY2hhcmFjdGVyaXN0aWNzX2NoMWAgY29sdW1uLCBhbmQgdGhlbiBpbnNlcnQgaW50byBjb3JyZXNwb25kaW5nIFNldXJhdCBvYmplY3QncyBtZXRhZGF0YSBsYXllci4KCmBgYHtyfQojIExvYWQgR1NNcyBtZXRhZGF0YSBmcm9tIEdFT21ldGFkYgpnc21fbGlzdF9zdHJpbmcgPC0gcGFzdGUocGFzdGUwKCInIiwgc2NfZ3NtX2xpc3QsICInIiksIGNvbGxhcHNlID0gIiwiKQpzcWwgPC0gcGFzdGUwKAogICJTRUxFQ1QgZ3NtLCB0aXRsZSwgY2hhcmFjdGVyaXN0aWNzX2NoMSAiLAogICJGUk9NIGdzbSAiLAogICJXSEVSRSBnc20gSU4gKCIsIGdzbV9saXN0X3N0cmluZywgIikiCikKbWV0YWRhdGEgPC0gZGJHZXRRdWVyeShjb24sIHNxbCkKCiMgTWVyZ2UgdGhlIGNvbmRpdGlvbiBtZXRhZGF0YSB0byBjb3JyZXNwb25kaW5nIFNldXJhdCBvYmoncyBtZXRhZGF0YQptZXRhZGF0YSRDb25kaXRpb24gPC0gc3ViKCIuKmNvbmRpdGlvbjpcXHMqKFteO10rKS4qIiwgIlxcMSIsIG1ldGFkYXRhJGNoYXJhY3RlcmlzdGljc19jaDEsIGlnbm9yZS5jYXNlID0gVFJVRSkKCiMgTG9vcCBhbmQgYXNzaWduCmZvciAoZ3NtIGluIG5hbWVzKHNldXJhdF9saXN0KSkgewogICMgRmluZCB0aGUgY29uZGl0aW9uIGZvciB0aGlzIEdTTQogIHZhbCA8LSBtZXRhZGF0YSRDb25kaXRpb25bbWV0YWRhdGEkZ3NtID09IGdzbV0KICAKICAjIEFzc2lnbiB0byBTZXVyYXQgb2JqZWN0IChoYW5kbGluZyBtaXNzaW5nIHZhbHVlcykKICBpZiAobGVuZ3RoKHZhbCkgPiAwKSB7CiAgICBzZXVyYXRfbGlzdFtbZ3NtXV0kQ29uZGl0aW9uIDwtIHZhbAogIH0gZWxzZSB7CiAgICBzZXVyYXRfbGlzdFtbZ3NtXV0kQ29uZGl0aW9uIDwtICJVbmtub3duIgogIH0KfQoKIyBGaW5hbCBNZXJnZSBhbGwgc2FtcGxlcyBpbnRvIG9uZSBzZXVyYXQgb2JqCmNvbWJpbmVkX3NldXJhdCA8LSBtZXJnZSgKIHggPSBzZXVyYXRfbGlzdFtbMV1dLCAKIHkgPSBzZXVyYXRfbGlzdFstMV0sIAogYWRkLmNlbGwuaWRzID0gbmFtZXMoc2V1cmF0X2xpc3QpLCAKIHByb2plY3QgPSAiR1NFMjA5NTUyIgopCiMgVmVyaWZ5CnRhYmxlKGNvbWJpbmVkX3NldXJhdCRDb25kaXRpb24pCmBgYAoKIyMgRGF0YSBleHBsb3JpYXRpb24KClBsb3QgdGhlIGZvbGxvd2luZyBzdGF0aXN0aWNzOgoKLSAgIE51bWJlciBvZiB1bmlxdWUgaWRlbnRpZmllciAoaW5mZXIgY292ZXJhZ2UpCi0gICBOdW1iZXIgb2YgdG90YWwgYW1vdW50IG9mIGlkZW50aWZpZXIgKGluZmVyIGRlcHRoKQotICAgTnVtYmVyIG9mIHNhbXBsZXMKLSAgIE51bWJlciBvZiBjZWxscwoKYGBge3IgcWMtcGxvdC1wZXItY29uZGl0aW9uLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9MTQsIGZpZy5hbGlnbj0nY2VudGVyJywgZHBpPTMwMCwgb3V0LndpZHRoPSIxMDAlIn0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBhdGNod29yaykKCnRoZW1lX3NldCh0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDI2KSkKCiMgMS4gQ292ZXJhZ2UgKG5GZWF0dXJlX1JOQSkKcF9jb3YgPC0gVmxuUGxvdCgKICBjb21iaW5lZF9zZXVyYXQsIAogIGZlYXR1cmVzID0gIm5GZWF0dXJlX1JOQSIsIAogIGdyb3VwLmJ5ID0gIkNvbmRpdGlvbiIsIAogIHB0LnNpemUgPSAwCikgKyAKICBnZ3RpdGxlKCJDb3ZlcmFnZSIpICsgCiAgeWxhYigibkZlYXR1cmVfUk5BIikKCiMgMi4gRGVwdGggKG5Db3VudF9STkEpIHdpdGggYWRqdXN0ZWQgYXhpcwpwX2RlcHRoIDwtIFZsblBsb3QoCiAgY29tYmluZWRfc2V1cmF0LCAKICBmZWF0dXJlcyA9ICJuQ291bnRfUk5BIiwgCiAgZ3JvdXAuYnkgPSAiQ29uZGl0aW9uIiwgCiAgcHQuc2l6ZSA9IDAKKSArIAogIGdndGl0bGUoIkRlcHRoIikgKyAKICB5bGFiKCJuQ291bnRfUk5BIikgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDUwMDAwKSkgCgojIENvbWJpbmUgdGhlbSB0byByZWNyZWF0ZSBwX21ldHJpY3MKcF9tZXRyaWNzIDwtIHBfY292IHwgcF9kZXB0aAoKIyBOdW1iZXIgb2YgY2VsbHMgYW1vbmcgY29uZGl0aW9ucwptZXRhX2RhdGEgPC0gY29tYmluZWRfc2V1cmF0QG1ldGEuZGF0YQoKIyBBLiBCYXIgcGxvdDogVG90YWwgTnVtYmVyIG9mIENlbGxzIHBlciBDb25kaXRpb24KcF9jZWxscyA8LSBnZ3Bsb3QobWV0YV9kYXRhLCBhZXMoeCA9IENvbmRpdGlvbiwgZmlsbCA9IENvbmRpdGlvbikpICsKICBnZW9tX2Jhcihjb2xvciA9ICJibGFjayIpICsKICBnZW9tX3RleHQoc3RhdCA9ICdjb3VudCcsIGFlcyhsYWJlbCA9IGFmdGVyX3N0YXQoY291bnQpKSwgdmp1c3QgPSAtMC41KSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBsYWJzKHRpdGxlID0gIlRvdGFsIENlbGxzIHBlciBHcm91cCIsIHkgPSAiTnVtYmVyIG9mIENlbGxzIiwgeCA9IE5VTEwpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIE51bWJlciBvZiBzYW1wbGVzIGFtb25nIGNvbmRpdGlvbnMKcF9zYW1wbGVzIDwtIG1ldGFfZGF0YSAlPiUKICBncm91cF9ieShDb25kaXRpb24pICU+JQogIHN1bW1hcml6ZShTYW1wbGVfQ291bnQgPSBuX2Rpc3RpbmN0KG9yaWcuaWRlbnQpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBDb25kaXRpb24sIHkgPSBTYW1wbGVfQ291bnQsIGZpbGwgPSBDb25kaXRpb24pKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBTYW1wbGVfQ291bnQpLCB2anVzdCA9IC0wLjUpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGxhYnModGl0bGUgPSAiVG90YWwgU2FtcGxlcyAoR1NNcykgcGVyIEdyb3VwIiwgeSA9ICJOdW1iZXIgb2YgU2FtcGxlcyAoR1NNcykiLCB4ID0gTlVMTCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgTWVyZ2UgcGxvdHMKZmluYWxfcGxvdCA8LSBwX21ldHJpY3MgLyAocF9zYW1wbGVzIHwgcF9jZWxscykKCiMgQWRqdXN0IGZvbnQgc2l6ZQpmaW5hbF9wbG90IDwtIGZpbmFsX3Bsb3QgJiB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE2KQpmaW5hbF9wbG90CmBgYAoKKipGaWd1cmUgMToqKiBPdmVyYWxsIHN0YXRpc3RpY3Mgb2YgZGF0YXNldCBgR1NFMjA5NTUyYCBncm91cGVkIGJ5IGNvbmRpdGlvbiAoQ29udHJvbCB2LnMuIHRiaSkuIFRoZSB1cHBlci1sZWZ0IHBhbmVsIGluZGljYXRlIHRoZSBudW1iZXIgb2YgdW5pcXVlIGlkZW50aWZpZXI7IFRoZSB1cHBlci1yaWdodCBwYW5lbCBpbmRpY2F0ZSB0aGUgdG90YWwgYW1vdW50IG9mIGlkZW50aWZpZXIsIHRoZSBtYXhpbXVtIGFtb3VudCBvZiBpZGVudGlmaWVyIHdhcyBsaW1pdGVkIHVwIHRvIDUwMDAwIGZvciB0aGUgc2FrZSBvZiBhZXN0aGV0aWNzOyBUaGUgbG93ZXItbGVmdCBwYW5lbCBpbmRpY2F0ZSBudW1iZXIgb2Ygc2FtcGxlcyAoR1NNcyk7IFRoZSBsb3dlci1yaWdodCBwYW5lbCBpbmRpY2F0ZSB0aGUgbnVtYmVyIG9mIGNlbGxzIjsgWC1heGlzIG9mIGFsbCBmb3VyIHBhbmVsIGluZGljYXRlIGNvbmRpdGlvbi4KCkdlbmVyYWxseSwgY29udHJvbCBncm91cCBoYXZlIGhpZ2hlciBkZXB0aCBhbmQgY292ZXJhZ2UsIGJ1dCBsZXNzIHNhbXBsZSBhbmQgY2VsbHMuIAoKVGhlbiBwbG90IHRoZSBzdGF0aXN0aWNzIGJlbG93IHBlciBzYW1wbGUgKEdTTSk6CgotICAgTnVtYmVyIG9mIHVuaXF1ZSBpZGVudGlmaWVyIChpbmZlciBjb3ZlcmFnZSkKLSAgIE51bWJlciBvZiB0b3RhbCBhbW91bnQgb2YgaWRlbnRpZmllciAoaW5mZXIgZGVwdGgpCi0gICBOdW1iZXIgb2YgY2VsbHMKCmBgYHtyIHFjLXBsb3QtcGVyLXNhbXBsZSwgZmlnLndpZHRoPTE0LCBmaWcuaGVpZ2h0PTE0LCBmaWcuYWxpZ249J2NlbnRlcicsIGRwaT0zMDAsIG91dC53aWR0aD0iMTAwJSJ9CgojIERlZmluZSB0aGUgdmVydGljYWwgbGluZSBzdHlsZSBvbmNlIHRvIGtlZXAgaXQgY29uc2lzdGVudAp2ZXJ0aWNhbF9saW5lIDwtIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDUuNSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDEpCgojIDEuIENvdmVyYWdlIChuRmVhdHVyZV9STkEpCnBfY292IDwtIFZsblBsb3QoCiAgY29tYmluZWRfc2V1cmF0LCAKICBmZWF0dXJlcyA9ICJuRmVhdHVyZV9STkEiLCAKICBncm91cC5ieSA9ICJvcmlnLmlkZW50IiwgCiAgcHQuc2l6ZSA9IDAKKSArIAogIGdndGl0bGUoIkNvdmVyYWdlIikgKyAKICB5bGFiKCJuRmVhdHVyZV9STkEiKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpICMgT2Z0ZW4gY2xlYW5lciB0byByZW1vdmUgeC1sYWJlbCBpZiB0ZXh0IGlzIHJvdGF0ZWQKICApICsgdmVydGljYWxfbGluZQoKIyAyLiBEZXB0aCAobkNvdW50X1JOQSkgd2l0aCBhZGp1c3RlZCBheGlzCnBfZGVwdGggPC0gVmxuUGxvdCgKICBjb21iaW5lZF9zZXVyYXQsIAogIGZlYXR1cmVzID0gIm5Db3VudF9STkEiLCAKICBncm91cC5ieSA9ICJvcmlnLmlkZW50IiwgCiAgcHQuc2l6ZSA9IDAKKSArIAogIGdndGl0bGUoIkRlcHRoIikgKyAKICB5bGFiKCJuQ291bnRfUk5BIikgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDUwMDAwKSkgKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKQogICkgKyB2ZXJ0aWNhbF9saW5lCgojIEEuIEJhciBwbG90OiBUb3RhbCBOdW1iZXIgb2YgQ2VsbHMgcGVyIENvbmRpdGlvbgptZXRhX2RhdGEgPC0gY29tYmluZWRfc2V1cmF0QG1ldGEuZGF0YSAjIEVuc3VyZSBtZXRhX2RhdGEgaXMgZGVmaW5lZApwX2NlbGxzIDwtIGdncGxvdChtZXRhX2RhdGEsIGFlcyh4ID0gb3JpZy5pZGVudCwgZmlsbCA9IG9yaWcuaWRlbnQpKSArCiAgZ2VvbV9iYXIoY29sb3IgPSAiYmxhY2siKSArCiAgZ2VvbV90ZXh0KHN0YXQgPSAnY291bnQnLCBhZXMobGFiZWwgPSBhZnRlcl9zdGF0KGNvdW50KSksIHZqdXN0ID0gLTAuNSkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBDZWxscyBwZXIgU2FtcGxlIiwgeSA9ICJOdW1iZXIgb2YgQ2VsbHMiLCB4ID0gTlVMTCkgKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIgogICkgKwogIGNvb3JkX2ZpeGVkKHJhdGlvID0gMC4wMDA1KSAgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDYwMDApKSArIHZlcnRpY2FsX2xpbmUKCiMgTWVyZ2UgcGxvdHMKZmluYWxfcGxvdCA8LSBwX2NvdiAvIHBfZGVwdGggLyBwX2NlbGxzICsgCiAgcGxvdF9sYXlvdXQoaGVpZ2h0cyA9IGMoMSwgMSwgMSkpCgojIEFkanVzdCBmb250IHNpemUgKGFuZCByZS1hcHBseSB0aGVtZSBlbGVtZW50cyBpZiBuZWVkZWQgYnkgdGhlIGJhc2UgdGhlbWUpCmZpbmFsX3Bsb3QgPC0gZmluYWxfcGxvdCAmIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTYpICYgCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiCiAgKQoKZmluYWxfcGxvdApgYGAKKipGaWd1cmUgMjoqKiBPdmVyYWxsIHN0YXRpc3RpY3MgcGVyIHNhbXBsZSAoR1NNKS4gVGhlIHRvcCBwYW5lbCBpbmRpY2F0ZSBudW1iZXIgb2YgdW5pcXVlIGlkZW50aWZpZXI7IFRoZSBtaWRkbGUgcGFuZWwgaW5kaWNhdGUgbnVtYmVyIG9mIHRvdGFsIGlkZW50aWZpZXI7IFRoZSBib3R0b20gcGFuZWwgaW5kaWNhdGUgbnVtYmVyIG9mIGNlbGxzOyBYLWF4aXMgb2YgYWxsIHRocmVlIHBhbmVsIGluZGljYXRlIHNhbXBsZXMgKEdTTXMpOyBUaGUgdmVydGlhbCByZWQgZGFzaCBsaW5lIHNlcGVhdGUgY29udHJvbCAobGVmdCBzaWRlIG9mIHJlZCBsaW5lKSwgYW5kIHRiaSAocmlnaHQgc2lkZSBvZiByZWQgbGluZSkuCgpJbiBnZW5lcmFsLCB0aGUgc2VxdWVuY2UgZGVwdGggYW5kIGNvdmVyYWdlIGlzIG1vcmUgY29uc2lzdGVuY2UgYW1vbmcgY29udHJvbCBncm91cC4gVGJpIGdyb3VwIGhhcyBtb3JlIHZhcmlhdGVkIGRlcHRoIGFuZCBjb3ZlcmFnZSB3aXRoIGBHU002Mzc2ODFgIGJlaW5nIHRoZSBzYW1wbGUgb2YgbG93ZXN0IGRlcHRocyBhbmQgY292ZXJhZ2UuIFRoZSBudW1iZXIgb2YgY2VsbHMgd2l0aGluIGRpZmZlcmVudCBzYW1wbGUgYWxzbyB2YXJpZXMsIGZyb20gaGlnaGVzdCAzNzA4IChgR1NNNjM3NjgyMmApIHRvIGxvd2VzdCAzNTggKGBHU002Mzc2ODIwYCkuIFRoaXMgaW1iYWxhbmNlIGluIGNlbGwgbnVtYmVyIG1heSByZXN1bHQgaW4gZG93bnN0cmVhbSBhbmFseXNpcyBkb21pbmF0ZSBieSBzYW1wbGUgd2hpY2ggY29udGFpbiBtb3JlIGNlbGxzLgoKQW5vdGhlciBjb21tb24gcXVhbGl0eSBjb250cm9sIHN0ZXAgaW4gc2NSTkEtc2VxIGlzIHRvIHJlbW92ZSBgZGFtYWdlZCBjZWxsc2AuIFdoZW4gZGFtYWdlZCBjZWxscyB3ZXJlIHNlcXVlbmNlZCBvciBkYW1hZ2VkIGR1cmluZyBzZXF1ZW5jaW5nLCB0aGUgZXhwcmVzc2lvbiBpbnRlbnNpdHkgd2lsbCBzY2FsZWQgZG93biBkdWUgdG8gbGVha2VkIFJOQSwgdGh1cyBzaWduaWZpY2FudGx5IGJpYXMgZG93bnN0cmVhbSBjbHVzdGVyaW5nLCBhbm5vdGF0aW9uLCBhbmQgZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBhbmFseXNpcy4gQSBjb21tb24gbWV0aG9kIHRvIGNvbmR1Y3QgdGhpcyBmaWx0ZXJpbmcgaXMgdXNpbmcgYG1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdCBwZXJjZW50YWdlYCwgYmFzZWQgb24gdGhpcyBpZGVhIHRoYXQgaWYgYSBjZWxsIGhhcyBsZWFrZWQgUk5BLCB0aGUgYG1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdCBwZXJjZW50YWdlYCB3aWxsIGJlIHJlbGF0aXZlbHkgaGlnaGVyIHNpbmNlIG1pdG9jaG9uZHJpYSBpcyByZWxhdGl2ZWx5IGxhcmdlIGFuZCBsZXNzIGxpa2VseSB0byBsZWFrLgoKYGBge3IgbXQtcGxvdCwgZmlnLndpZHRoPTE0LCBmaWcuaGVpZ2h0PTE0LCBmaWcuYWxpZ249J2NlbnRlcicsIGRwaT0zMDAsIG91dC53aWR0aD0iMTAwJSJ9CiMgVXNlIHNldXJhdCBQZXJjZW50YWdlRmVhdHVyZVNldCgpIGZ1bmN0aW9uCiMgQ3JlYXRlIGEgbmV3IGVudHJ5IGluIG1ldGEuZGF0YSBsYXllciBzdG9yaW5nIG1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdCBwZXJjZW50YWdlCmNvbWJpbmVkX3NldXJhdFtbInBlcmNlbnQubXQiXV0gPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQoY29tYmluZWRfc2V1cmF0LCBwYXR0ZXJuID0gIl5NVC0iKQpWbG5QbG90KGNvbWJpbmVkX3NldXJhdCwgZmVhdHVyZXMgPSBjKCJwZXJjZW50Lm10IikpICsKICB0aGVtZSgKICAgICMgUm90YXRlIHgtYXhpcyBsYWJlbHMgNDUgZGVncmVlcyBzbyB0aGV5IGRvbid0IG92ZXJsYXAKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIgIyBIaWRlIGxlZ2VuZCBzaW5jZSB4LWF4aXMgYWxyZWFkeSBzaG93cyB0aGUgbmFtZXMKICApICsgdmVydGljYWxfbGluZQpgYGAKKipGaWd1cmUgMzoqKiBNaXRvY2hvbmRyaWFsIHRyYW5zY3JpcHQgcGVyY2VudGFnZSBncm91cGVkIGJ5IEdTTXMuIFRoZSBZLWF4aXMgaW5kaWNhdGUgbWl0b2Nob25kcmlhbCB0cmFuc2NyaXB0IHBlcmNlbnRhZ2U7IFRoZSBYLWF4aXMgaW5kaWNhdGUgR1NNOyBUaGUgdmVydGlhbCByZWQgZGFzaCBsaW5lIHNlcGVhdGUgY29udHJvbCAobGVmdCBzaWRlIG9mIHJlZCBsaW5lKSwgYW5kIHRiaSAocmlnaHQgc2lkZSBvZiByZWQgbGluZSkuCgpUaGUgbWl0b2Nob25kcmlhbCB0cmFuc2NyaXB0IHBlcmNlbnRhZ2UgYWxpZ25lZCB3aXRoIHByZXZpb3VzIGRlcHRoIGFuZCBjb3ZlcmFnZSBhbW9uZyBzYW1wbGVzLCB3aGVyZSBjb250cm9sIGdyb3VwIHRlbmQgdG8gYmUgbW9yZSBjb25zaXN0ZW50LiBBIGh5cG90aGVzaXMgaXMgdGhhdCBUcmF1bWF0aWMgQnJhaW4gSW5qdXJ5IChUQkkpIGlzIGEgYnJvYWQgdGVybSB0aGF0IGluY2x1ZGUgdmFyaW91cyBicmFpbiBkYW1hZ2UgcmVzdWx0IGZyb20gZXh0ZXJuYWwgZm9yY2UsIGFuZCB0aGUgaW50ZW5zaXR5IGFuZCBleGFjdCBwYXRob2xvZ3kgbWF5IHZhcnkgYW1vbmcgZGlmZmVyZW50IHRiaSBzYW1wbGUsIHJlZmxlY3RlZCBieSBzZXF1ZW5jZSBkZXB0aCBhbmQgY292ZXJhZ2UuIE5vdGljZWFibHksIGBHU002Mzc2ODE0YCBoYXMgcmVsYXRpdmVseSBoaWdoZXIgbWl0b2Nob25kcmlhbCB0cmFuc2NyaXB0IHBlcmNlbnRhZ2UsIGFuZCByZWNhbGwgdGhhdCBpdCdzIGFsc28gdGhlIHNhbXBsZSBoYXZpbmcgbG93ZXN0IGRlcHRoIGFuZCBjb3ZlcmFnZSwgd2hpY2ggbWlnaHQgaW1wbHkgZXhpc3RpbmcgdGVjaG5pY2FsIHZhcmlhbmNlIHRoYXQgY291bGQgYmlhcyBkb3duc3RyZWFtIGFuYWx5c2lzLiA=